#include <memory.h>

# SH_PG_SIZE - Shift to get PPN from index into page table.
# SH_PG_NUM - Shift to get addres offset of PPN from index.
# SH_PG_DIR - Shift to get PPN from virtual address.
#define SH_PG_SIZE	0xC
#define SH_PG_NUM	0x2
#define SH_PG_DIR	0x14

#define CR0_PG		0x80000000

.text
# _start specifies the ELF entry point. Since we haven't set up
# virtual memory when the bootloader enters this code, we need the
# bootloader to jump to the physical address of the entry point.
.globl _start
_start = KERN_V2P(entry)

.globl entry
entry:
	
	# Set up a simple page table that maps the first 4MB of memory
	movl	$PG_NUM,%ecx
init_pgtable:							# Loop over PTEs
	subl	$0x1,%ecx
	movl	%ecx,%eax
	shl		$SH_PG_SIZE,%eax
	orl		$(PG_PRESENT | PG_RW),%eax	# EAX holds the PTE
	movl	%ecx,%edx
	shl		$SH_PG_NUM,%edx
	addl	$pgtable,%edx				# EDX holds the location of PTE
	movl	%eax,(%edx)					# write PTE to appropriate location
	cmpl	$0x0,%ecx
	jnz		init_pgtable
	
	# Set up a page diretory that maps
	#	* virtual [0:4MB] to physical [0:4MB]
	#	* virtual [KERNBASE:KERNBASE+4MB] to physical [0:4MB]
init_pgdir:
	# Page directory entry that points to pgtable
	movl	$pgtable,%eax
	orl		$(PG_PRESENT | PG_RW),%eax
	
	# Virtual [0:4MB] to physical [0:4MB]
	movl	%eax,pgdir
	
	# Virtual [KERNBASE:KERNBASE+4MB] to physical [0:4MB]
	movl	%eax,(KERNBASE>>SH_PG_DIR+pgdir)

	# Turn on paging
	movl	$pgdir,%eax
	movl	%eax,%cr3
	movl	%cr0,%eax
	orl		$CR0_PG,%eax
	movl	%eax,%cr0

	# Now paging is enabled, but we're still running at a low EIP
	# Jump up above KERNBASE before entering C code.
	movl	$relocated,%eax
	jmp		*%eax

relocated:

	# The bootloader passed the amount of RAM (in MB) in EBX
	movl	%ebx,bootloader_memsize
	
	# Clear the stack pointer so that stack backtraces can terminate
	movl	$0x0,%ebp
	
	# Set up kernel stack
	movl	$kernelstacktop,%esp

	# Call into main, should never return
	call	init

spin:	
	jmp		spin

# Holds the amount of RAM (in MB) which is passed by the bootloader in EBX.
.globl bootloader_memsize
bootloader_memsize:
	.word	0x0
	.word	0x0

.data
# Page directory and page tables must be alligned to a page boundary.
.p2align 12
pgdir:
	.space	PG_SIZE

pgtable:
	.space	PG_SIZE

kernelstack:
	.space	KSTACKSIZE

kernelstacktop: